package nl.zuiderfestival.raydance.model.net;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import nl.zuiderfestival.raydance.model.RayDance;
import nl.zuiderfestival.raydance.model.zones.Zone;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import TUIO.TuioClient;
import TUIO.TuioCursor;
import TUIO.TuioListener;
import TUIO.TuioObject;
import TUIO.TuioTime;

public class TuioEventTranslator implements TuioListener {
	
	private static final Logger log = LoggerFactory.getLogger(TuioEventTranslator.class);
	
	private Map<Zone, List<TuioCursor>> zoneMovement;
	
	private RayDance model;

	private int cursorCount;

	private TuioClient client;

	public TuioEventTranslator(RayDance model) {
		this.model = model;
		zoneMovement = new HashMap<Zone, List<TuioCursor>>();
		cursorCount=0;
	}

	public void addClient(TuioClient client) {
		if (this.client != null) {
			cleanUpCheck();
			log.debug("Resetting for new client.");
		}
		this.client = client;
	}

	public void addTuioCursor(TuioCursor cursor) {
		log.debug("addTuioCursor: {} {} {}", new Object[] {cursor, cursor.getX(), cursor.getY()});
		double x = cursor.getX() * model.getScale();
		double y = cursor.getY() * model.getScale();
		
		List<Zone> zones = model.getZonesFor(x, y);
		for (Zone zone : zones) {
			addMovementFor(zone, cursor);
		}
		cursorCount++;
	}

	public void removeTuioCursor(TuioCursor cursor) {
		log.debug("removeTuioCursor: {} {} {}", new Object[] {cursor, cursor.getX(), cursor.getY()});
		double x = cursor.getX() * model.getScale();
		double y = cursor.getY() * model.getScale();
		
		List<Zone> zones = model.getZonesFor(x, y);
		for (Zone zone : zones) {
			removeMovementFor(zone, cursor);
		}
		
		cursorCount--;
		cleanUpCheck();
	}

	public void updateTuioCursor(TuioCursor cursor) {
		log.debug("updateTuioCursor: {} {} {}", new Object[] {cursor, cursor.getX(), cursor.getY()});
		double x = cursor.getX() * model.getScale();
		double y = cursor.getY() * model.getScale();
		
		List<Zone> removalCandidates = getZonesFor(cursor);
		for (Zone zone : removalCandidates) {
			if (!zone.isIn(x, y)) {
				removeMovementFor(zone, cursor);
			}
		}
		
		List<Zone> newMovementCandidates = model.getZonesFor(x, y);
		for (Zone zone : newMovementCandidates) {
			addMovementFor(zone, cursor);
		}
	}

	public void refresh(TuioTime time) {}
	public void addTuioObject(TuioObject object) {}
	public void removeTuioObject(TuioObject object) {}
	public void updateTuioObject(TuioObject object) {}

	private void addMovementFor(Zone zone, TuioCursor cursor) {
		if (!zoneMovement.keySet().contains(zone)) {
			zoneMovement.put(zone, new LinkedList<TuioCursor>());
		}
		
		List<TuioCursor> cursors = zoneMovement.get(zone);
		if (!cursors.contains(cursor)) {
			cursors.add(cursor);
		}
		if (!zone.isEnabled()) {
			log.info("New movement in zone {}.", new Object[] {zone});
		}
		zone.setEnabled(true);
	}
	
	private void removeMovementFor(Zone zone, TuioCursor cursor) {
		List<TuioCursor> cursors = zoneMovement.get(zone);
		if (cursors != null) {
			if (cursors.contains(cursor)) {
				cursors.remove(cursor);
			}
			if (cursors.size() == 0) {
				if (zone.isEnabled()) {
					log.info("No more movement in zone {}.", new Object[] {zone});
				}
				zone.setEnabled(false);
			}
		}
	}

	private List<Zone> getZonesFor(TuioCursor cursor) {
		LinkedList<Zone> result = new LinkedList<Zone>();
		for (Zone zone : zoneMovement.keySet()) {
			List<TuioCursor> cursors = zoneMovement.get(zone);
			if (cursors.contains(cursor)) {
				result.add(zone);
			}
		}
		return result;
	}

	private void cleanUpCheck() {
		if (cursorCount+1 != client.getTuioCursors().size()) {
			log.warn("According to me there are {} cursors, but the client says there are {}", new Object[] {cursorCount-1, client.getTuioCursors().size()-1});
			if (client.getTuioCursors().size()-1 == 0) {
				for (Zone zone : zoneMovement.keySet()) {
					List<TuioCursor> cursors = zoneMovement.get(zone);
					cursors.clear();
					zone.setEnabled(false);
					log.warn("Removed all floating cursors for zone {}.", new Object[] {zone});
				}
			}
		}
	}

}
